package com.xcity.game.activity.invite;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import com.xcity.db.entity.Invite;
import com.xcity.db.entity.Player;
import com.xcity.db.entity.User;
import com.xcity.db.mapper.InviteMapper;
import com.xcity.game.account.AccountChannel;
import com.xcity.game.account.AccountService;
import com.xcity.game.activity.ActivityService;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.item.ItemService;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.msg.BatchItemsPush;
import com.xcity.pomelo.request.InviteGetRewardRequest;
import com.xcity.pomelo.request.InviteInfoRequest;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.InviteGetRewardResponse;
import com.xcity.pomelo.response.InviteInfoResponse;
import com.xcity.util.DBUtils;

import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.ServiceException;
import naga.x.util.NumberUtils;
import naga.x.util.StringUtils;

/**
 * 邀请玩家活动
 * @author yang.li
 *
 */
@OPHandler
public class InviteActivityService extends ActivityService implements EventListener, Updatable {

	protected Queue<Invite> pendingNewInvites = new LinkedBlockingQueue<Invite>();
//	protected TLongIntMap invitesCounter = new TLongIntHashMap(64, 0.5f, -1, -1);
	protected Map<Long, List<Invite>> inviteLookup = new ConcurrentHashMap<Long, List<Invite>>(32);
	
	private static final int DEFAULT_UPDATE_INTERVAL = 2000;
	private static final int FLUSH_SIZE = 500;
	private long lastFlushTime;
	private static final int MAX_INVITE_SIZE = 100; // 最大可邀请数量
	private static final int STATE_UNREWARD = 0, STATE_REWARDED = 1;
	
	@Override
	public void startup() throws ServiceException {
		App.getApp().getUpdater().addAsync(this);
		App.getApp().getEventManager().register(this);
	}

	@Override
	public void shutdown() throws ServiceException {
		flush(true);
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] {
				GameEvents.EVENT_PLAYER_REGISTER,
				GameEvents.EVENT_PLAYER_LOADED,
				GameEvents.EVENT_PLAYER_LOGOUT
		};
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_PLAYER_REGISTER:
				addInvite(event.getParameter(1), event.getParameter(0));
				break;
			case GameEvents.EVENT_PLAYER_LOADED:
				playerLoaded(event.getParameter(0));
				break;
			case GameEvents.EVENT_PLAYER_LOGOUT:
//				counterRemove(event.getParameter());
				removeFromLookup(event.getParameter(0));
				break;
		}
	}
	
	public void addInvite(final String inviter, final Player invitee) {
		if (StringUtils.isEmpty(inviter)) {
			return;
		}
		App.getApp().getThreadPool().execute(new Runnable() {
			@Override
			public void run() {
				AccountChannel channel = invitee.getAccount().getChannel();
				PlayerCacheService service = App.getApp().getServiceManager().get(PlayerCacheService.class);
				Player player = null;
				Inviter inviterObj = Inviter.parse(inviter, channel);
				if (inviterObj.playerId != -1) {
					player = service.find(inviterObj.playerId);
				}
				if (player == null && inviterObj.accId != null) {
					User acc = App.getApp().getServiceManager().get(AccountService.class).find(channel.withPrefix(inviterObj.accId));
					player = acc != null ? service.find(acc.getDefaultPlayerId()) : null;
				}
				if (player == null) {
					return;
				}
//				// 1.将inviter当做player id处理
//				long playerId = NumberUtils.parseLong(inviter, -1);
//				Player player = playerId != -1 && channel != AccountChannel.J9KGAMES ? service.find(playerId) : null; // 金榜游戏渠道直接将inviter当做account id处理
//				// 2.未找到player则再将inviter当做account id处理
//				if (player == null) {
//					String channelUid = channel.withPrefix(inviter);
//					User account = App.getApp().getServiceManager().get(AccountService.class).getAccount(channelUid, true);
//					player = service.find(account != null ? account.getDefaultPlayerId() : playerId);
//					if (player == null) {
//						return;
//					}
//				}
				if (player.getId() != invitee.getId()) { // 不能自己邀请自己
					pendingNewInvites.offer(new Invite(player.getId(), invitee.getId()));
				}
			}
		});
	}
	
	@Override
	public boolean update() {
		if (TimeUpdater.getInstance().now() - lastFlushTime > DEFAULT_UPDATE_INTERVAL) {
			lastFlushTime = TimeUpdater.getInstance().now();
			flush(false);
		}
		return true;
	}
	
	private void flush(boolean force) {
		Queue<Invite> queue = pendingNewInvites;
		int count = 0;
		Invite invite;
		while ((invite = queue.poll()) != null) {
			InviteMapper mapper = DBUtils.getMapper(InviteMapper.class);
			try {
				Invite exist;
				if ((exist = mapper.findByInvitee(invite.getInvitee())) != null) {
					LOG.info("[INVITE FAIL]TRY[{}->{}]INVITER[{}]", invite.getInviter(), invite.getInvitee(), exist.getInviter());
				} else {
//					counterAdd(mapper, invite.getInviter(), 1);
					mapper.save(invite);
					addToLookup(invite);
					LOG.info("[INVITE SUCC]INVITER[{}]INVITEE[{}]", invite.getInviter(), invite.getInvitee());
				}
			} catch (Exception e) {
				LOG.error("[INVITE ERROR]INVITER[{}]INVITEE[{}]",invite.getInviter(), invite.getInvitee());
				LOG.error(e.getMessage(), e);
			} finally {
				DBUtils.close();
			}
			if (!force && ++count == FLUSH_SIZE) {
				break;
			}
		}
	}
	
	private void addToLookup(Invite invite) {
		Long key = Long.valueOf(invite.getInviter());
		List<Invite> list = inviteLookup.get(key);
		if (list != null) {
			list.add(invite);
		}
	}
	
	private void removeFromLookup(Player player) {
		inviteLookup.remove(player.getId());
	}
	
	private void playerLoaded(Player player) {
		Long key = Long.valueOf(player.getId());
		List<Invite> list = inviteLookup.get(key);
		InviteMapper mapper = DBUtils.getMapper(InviteMapper.class);
		try {
			list = mapper.findByInviter(player.getId());
			inviteLookup.put(key, list != null ? list : new ArrayList<Invite>());
		} finally {
			DBUtils.close();
		}
	}
	
//	private synchronized void counterAdd(InviteMapper mapper, long inviter, int addedVal) {
//		int oldVal = count(mapper, inviter);
//		invitesCounter.put(inviter, oldVal + addedVal);
//	}
//	
//	private synchronized void counterRemove(Player player) {
//		invitesCounter.remove(player.getId());
//	}
//	
//	private synchronized int count(InviteMapper mapper, long inviter) {
//		int c;
//		if ((c = invitesCounter.get(inviter)) == -1) {
//			try {
//				// not exist
//				InviteMapper _mapper = mapper != null ? mapper : DBUtils.getMapper(InviteMapper.class);
//				c = _mapper.count(inviter);
//				invitesCounter.put(inviter, c);
//			} finally {
//				if (mapper == null) {
//					DBUtils.close();
//				}
//			}
//		}
//		return c;
//	}
	
	@PomeloOP(PomeloRoute.INVITE_INFO)
	protected void requestInviteInfo(Session session, InviteInfoRequest req) {
		Player player = session.getClient();
//		int n = count(null, player.getId());
		List<Invite> invites = inviteLookup.get(player.getId());
		// 每一个二进制位表示对应档次奖励是否已领取[0:未领取,1:已领取]
		// 第一档(邀请1个玩家): state & 1, 第二档(邀请5个玩家): state >> 1 & 1, 第3档(邀请10个好友): state >> 2 & 1, ...
		int state = player.getPool().getIntValue(PlayerPool.PROPERTY_INVITE_STATE);
		session.send(new InviteInfoResponse(req.getSerial(), MAX_INVITE_SIZE, state, invites));
	}
	
	@PomeloOP(PomeloRoute.INVITE_REWARD_REQUEST)
	protected void requestGetReward(Session session, InviteGetRewardRequest req) {
		Player player = session.getClient();
//		int maxGrade = 21; // MAX_INVITE_SIZE / 5 + 1;
		int grade = req.getIndex();
		if (grade < 0) grade = 0;
		else if (grade >= 20) grade = 20;
		int c = getRequireCount(grade);
//		int n = count(null, player.getId());
		List<Invite> invites = inviteLookup.get(player.getId());
		if (invites == null || invites.size() < c) {
			session.send(new HintResponse(req.getSerial(), MessageFormat.format(MessageConst.INVITE_REWARD_NOT_REACH, c)));
			return;
		}
		int state = player.getPool().getIntValue(PlayerPool.PROPERTY_INVITE_STATE);
		int s = state >> grade & 1;
		if (s == STATE_REWARDED) {
			session.send(new HintResponse(req.getSerial(), MessageConst.REWARD_ALREADY_RECEIVED));
			return;
		}
		state |= STATE_REWARDED << grade;
		player.poolSet(PlayerPool.PROPERTY_INVITE_STATE, state);
		int stone = getRewardStone(grade);
		player.addStone(stone, Cause.INVITE);
		session.send(new InviteGetRewardResponse(req.getSerial(), state));
		
		BatchItemsPush sync = new BatchItemsPush(1);
		sync.add(ItemService.stoneDefault, stone);
		session.send(sync);
	}

	/**
	 * 获取某个档次所需邀请的玩家数量<br/>
	 * 最多21档<br/>
	 * 第1档:邀请1个玩家<br/>
	 * 第2档:邀请5个玩家<br/>
	 * 第3档:邀请10个玩家<br/>
	 * ...
	 * @param grade
	 * @return
	 */
	private int getRequireCount(int grade) {
		if (grade <= 0) {
			return 1;
		}
		int c = grade * 5;
		return c > MAX_INVITE_SIZE ? MAX_INVITE_SIZE : c;
	}
	
	private int getRewardStone(int grade) {
		if (grade <= 0) {
			return 10;
		}
		if (grade == 1) {
			return 40;
		}
		return 50;
	}
	
	static class Inviter {
		String accId; // 账号id（渠道用户id）
		long playerId; // 玩家id
		Inviter(String accId, long playerId) {
			this.accId = accId;
			this.playerId = playerId;
		}
		static Inviter parse(String inviter, AccountChannel channel) {
			if (channel == AccountChannel.QQ_WANBA || channel == AccountChannel.QQ) {
				// accId_playerId
				int n = inviter.indexOf('_');
				String accId = null;
				long playerId;
				if (n != -1) {
					accId = inviter.substring(0, n);
					playerId = NumberUtils.parseLong(inviter.substring(n + 1), -1);
				} else {
					playerId = NumberUtils.parseLong(inviter, -1);
					if (playerId == -1) {
						accId = inviter;
					}
				}
				return new Inviter(accId, playerId);
			} else {
				return new Inviter(inviter, NumberUtils.parseLong(inviter, -1));
			}
		}
	}
	
}
